package javacompiler.codegenerator;

import java.io.PrintStream;
import java.util.List;

public class CodeGenerator {
	
	private PrintStream stream;
	
	public CodeGenerator(PrintStream stream) {
		this.stream = stream;
	}
	
	public String generate(Instruction instruction) {
		String code = "";
		code = addLabel(instruction, code);
		code += instruction.getOperator().toCode() + " ";
		List<Register> registers = instruction.getRegisters();
		List<Double> constants = instruction.getConstants();
		
		switch (instruction.getOperator()) {
		case ADD: code = addOperation(code, registers); break; 
		case LOAD: code = loadOperation(instruction, code, registers, constants); break; 
		case BRZ: code = branchOnZeroOperation(instruction, code); break;
		case STORE: code = storeOperation(instruction, code); break;
		case MOVE: code = moveOperation(instruction, code, registers, constants); break;
		case SUB: code = subOperation(code, registers); break;
		case BLTZ: code = branchLessThanZeroOperation(instruction, code); break;
		case BGTZ: code = branchGreaterThanZeroOperation(instruction, code); break;
		case BRANCH: code = branchOperation(instruction, code); break;
		default: break; 
		}
		
		return code;
	}

	private String branchOperation(Instruction instruction, String code) {
		code += instruction.getLabel();
		return code;
	}

	private String branchGreaterThanZeroOperation(Instruction instruction, String code) {
		code += instruction.getLabel();
		code += ", ";
		code += instruction.getRegisters().get(0).getName();
		return code;
	}

	private String branchLessThanZeroOperation(Instruction instruction, String code) {
		code += instruction.getLabel();
		code += ", ";
		code += instruction.getRegisters().get(0).getName();
		return code;
	}

	private String subOperation(String code, List<Register> registers) {
		for (Register r : registers) {
			if (r != null) {
				// FIXME wrong
				code += r.getName();
				code += ", ";
			}
		}
		code = code.substring(0, code.length() - 2);
		return code;
	}

	private String moveOperation(Instruction instruction, String code,
			List<Register> registers, List<Double> constants) {
		code += registers.get(0).getName(); 
		code += ", ";

		if (instruction.getVariableName() != null) {
			code += instruction.getVariableName();			
		} else {
			code += constants.get(0);
		}
		return code;
	}

	private String addLabel(Instruction instruction, String code) {
		// FIXME ugly
		if (instruction.getLabel() != null && instruction.getOperator() != Instruction.AssemblyOperator.BRZ && 
											instruction.getOperator() != Instruction.AssemblyOperator.BLTZ && 
											instruction.getOperator() != Instruction.AssemblyOperator.BGTZ &&
											instruction.getOperator() != Instruction.AssemblyOperator.BRANCH) {
			code += instruction.getLabel() + ":";
		}
		return code;
	}

	private String loadOperation(Instruction instruction, String code, List<Register> registers, 
			List<Double> constants) {
		code = moveOperation(instruction, code, registers, constants);
		return code;
	}

	private String addOperation(String code, List<Register> registers) {
		for (Register r : registers) {
			if (r != null) {
				// FIXME wrong
				code += r.getName();
				code += ", ";
			}
		}
		code = code.substring(0, code.length() - 2);
		return code;
	}
	
	private String branchOnZeroOperation(Instruction instruction, String code) {
		code += instruction.getLabel();
		code += ", ";
		code += instruction.getRegisters().get(0).getName();
		return code;
	}

	private String storeOperation(Instruction instruction, String code) {
		code += instruction.getVariableName();
		code += ", ";
		code += instruction.getRegisters().get(0).getName();
		return code;
	}
	
	public void generateAndPrint(Instruction instruction) {
		String code = generate(instruction);
		stream.println(code);
	}
}
